# -*- coding: UTF-8 -*-
# 使用6.6.1版本的API
import sys
import time
from threading import Thread

import GlobalParam
from LogUtil import timePrintLog
from Function import judge_ret, get_tick
import json

if 'linux' in sys.platform:
    from CTP_API.linux import thosttraderapi as tdapi
if 'win' in sys.platform:
    from CTP_API.window import thosttraderapi as tdapi


class CTraderSpi(tdapi.CThostFtdcTraderSpi):
    def __init__(self, tduserapi):
        tdapi.CThostFtdcTraderSpi.__init__(self)
        self.tduserapi = tduserapi
        self.tdLogin_flag = False
        self.frontID = ''
        self.sessionID = ''
        self.tradingDay = ''
        self.maxOrderRef = ''

    def connect(self):
        try:
            # 创建SPI实例。CTraderSpi是继承自头文件中CThostFtdcTraderSpi的类型，
            # 用于收从CTP的回复，可以重写父类中的函数来实现自己的逻辑
            self.tduserapi = tdapi.CThostFtdcTraderApi_CreateFtdcTraderApi('./td/con_file/')
            self.tduserspi = CTraderSpi(self.tduserapi)
            # 将创建的SPI实例注册进实例，这样该API实例发出的请求对应的回报就会回调到对应的SPI实例的函数
            self.tduserapi.RegisterSpi(self)
            # 订阅共有流与私有流。订阅方式主要有三种，分为断点续传，重传和连接建立开始传三种。
            # TERT_RESTART：从本交易日开始重传。
            # TERT_RESUME：从上次收到的续传。
            # TERT_QUICK：只传送登录后的内容。
            self.tduserapi.SubscribePrivateTopic(tdapi.THOST_TERT_QUICK)
            self.tduserapi.SubscribePublicTopic(tdapi.THOST_TERT_QUICK)
            # 注册前置地址，是指将CTP前置的IP地址注册进API实例内
            self.tduserapi.RegisterFront(GlobalParam.trade_server_front)
            self.tduserapi.RegisterFront(GlobalParam.trade_server_front1)
            self.tduserapi.RegisterFront(GlobalParam.trade_server_front2)
            # API启动，init之后就会启动一个内部线程读写，并去连CTP前置
            self.tduserapi.Init()
            # Join函数是使得函数阻塞在这里，等待api实例创建的内部线程的结束。
            # 内部线程需要release才会释放结束
            self.tduserapi.Join()
        except Exception as e:
            print(e.__str__())

    # 连接前台
    def OnFrontConnected(self):
        try:
            timePrintLog("开始建立交易连接")
            authfield = tdapi.CThostFtdcReqAuthenticateField()
            authfield.BrokerID = GlobalParam.broker_id
            authfield.UserID = GlobalParam.investorID
            authfield.AppID = GlobalParam.appID
            authfield.AuthCode = GlobalParam.authcode
            ret = self.tduserapi.ReqAuthenticate(authfield, 0)
            if ret == 0:
                timePrintLog('发送穿透式认证请求成功！')
            else:
                timePrintLog('发送穿透式认证请求失败！')
                judge_ret(ret)
        except Exception as e:
            print(e.__str__())

    # 穿透式认证响应
    def OnRspAuthenticate(self, pRspAuthenticateField, pRspInfo, nRequestID: 'int', bIsLast: 'bool'):
        if pRspInfo.ErrorID != 0 and pRspInfo != None:
            timePrintLog('穿透式认证失败\n错误信息为：{}\n错误代码为：{}'.format(pRspInfo.ErrorMsg, pRspInfo.ErrorID))
        else:
            timePrintLog('穿透式认证成功！')

            # 发送登录请求
            loginfield = tdapi.CThostFtdcReqUserLoginField()
            loginfield.BrokerID = GlobalParam.broker_id

            loginfield.UserID = GlobalParam.investorID
            loginfield.Password = GlobalParam.password

            ret = self.tduserapi.ReqUserLogin(loginfield, 0)
            if ret == 0:
                timePrintLog('发送登录交易账户成功！')
            else:
                timePrintLog('发送登录交易账户失败！')
                judge_ret(ret)

    # 用户登录结果返回
    def OnRspUserLogin(self, pRspUserLogin, pRspInfo, nRequestID, bIsLast):
        if pRspInfo.ErrorID != 0 and pRspInfo != None:
            timePrintLog('登录交易账户失败\n错误信息为：{}\n错误代码为：{}'.format(pRspInfo.ErrorMsg, pRspInfo.ErrorID))
        else:
            timePrintLog('登录交易账户成功！')
            # 保存数据用于下单
            self.frontID = pRspUserLogin.FrontID
            self.sessionID = pRspUserLogin.SessionID
            self.maxOrderRef = int(pRspUserLogin.MaxOrderRef)
            # 保存交易日
            self.tradingDay = pRspUserLogin.TradingDay
            self.tdLogin_flag = True
            self.qryInstrument()
            # 启动一个新线程接收查询行情
            t0 = Thread(target=get_tick)
            t0.start()

    def OnRspQryInstrument(self, pInstrument, pRspInfo, nRequestID, bIsLast):
        if "&" not in pInstrument.InstrumentID:
            GlobalParam.ExchangeID[pInstrument.InstrumentID] = pInstrument.ExchangeID
        if bIsLast:
            with open('./instrument/ExchangeID.json', 'w', newline='\n', encoding='utf-8') as f:
                # json.dump(ExchangeID, f, ensure_ascii=False)
                data = json.dumps(GlobalParam.ExchangeID, indent=4, ensure_ascii=False)
                f.write(data)
            timePrintLog('查询合约完成')
            # 查询合约订阅
            self.reqQryDepthMarketData()

    def OnRspQryDepthMarketData(self, pDepthMarketData, pRspInfo, nRequestID, bIsLast):
        try:
            if "&" not in pDepthMarketData.InstrumentID:
                tikerMarketData = {'TradingDay': pDepthMarketData.TradingDay,
                                   'InstrumentID': pDepthMarketData.InstrumentID,
                                   'LastPrice': round(pDepthMarketData.LastPrice, 4),
                                   'BidPrice1': pDepthMarketData.BidPrice1,
                                   'AskPrice1': pDepthMarketData.AskPrice1,
                                   'PreSettlementPrice': pDepthMarketData.PreSettlementPrice,
                                   'PreClosePrice': pDepthMarketData.PreClosePrice,
                                   'SettlementPrice': pDepthMarketData.SettlementPrice,
                                   'ClosePrice': pDepthMarketData.ClosePrice,
                                   'UpperLimitPrice': round(pDepthMarketData.UpperLimitPrice, 4),
                                   'LowerLimitPrice': round(pDepthMarketData.LowerLimitPrice, 4),
                                   'UpdateTime': pDepthMarketData.UpdateTime}
                GlobalParam.tickQueue.put(tikerMarketData)

            if bIsLast:
                timePrintLog('查询行情数据返回完毕')
        except Exception as e:
            print(e)

    def qryInstrument(self):
        queryFile = tdapi.CThostFtdcQryInstrumentField()
        ret = self.tduserapi.ReqQryInstrument(queryFile, 0)
        if ret == 0:
            timePrintLog('发送查询合约成功！')
        else:
            timePrintLog('发送查询合约失败！')
            judge_ret(ret)
            while ret != 0:
                queryFile = tdapi.CThostFtdcQryInstrumentField()
                ret = self.tduserapi.ReqQryInstrument(queryFile, 0)
                timePrintLog('正在查询合约...')
                time.sleep(5)
        time.sleep(1)

    def reqQryDepthMarketData(self):
        queryParam = tdapi.CThostFtdcQryDepthMarketDataField()
        ret = self.tduserapi.ReqQryDepthMarketData(queryParam, 0)
        if ret == 0:
            timePrintLog('发送查询行情的请求成功！')
        else:
            print(ret)
            timePrintLog('发送查询行情的请求失败！')


def ConnectTrader():
    traderapi = CTraderSpi(None)
    traderapi.connect()


if __name__ == '__main__':
    ConnectTrader()
